﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace BoardController.Helper
{
    public partial class BoardController
    {
        public Board board;
        private AsyncServer netServer;

        List<Device> devices;
        private int delay;
        private CancellationTokenSource cancelTokenSource = new CancellationTokenSource();
        private bool isControlled = false;
        private IPAddress ipAddress;
        private int port;

        private int ControlOpenCount = 0;
        private int ControlCloseCount = 0;

        public Boolean baffleFlag = false;

        //private int GasOpenCount = 0;
        //private int GasCloseCount = 0;


        public BoardController(List<Device> _devices, IPAddress _ipaddress, int _port, int _delay)
        {
            board = (Board)_devices[0];

            devices = _devices;
            ipAddress = _ipaddress;
            port = _port;
            //netServer = new AsyncServer(ipAddress, port, 10);

            delay = _delay;
            isControlled = false;

            VitualDevice vitual = new VitualDevice(0);
            IMU imuLeft = new IMU(4);
            IMU imuRight = new IMU(5);
            TagCollection tags = new TagCollection(11);

            List<Device> Fandevices = new List<Device>();
            Fandevices.Add(vitual);
            Fandevices.Add(imuLeft);
            Fandevices.Add(imuRight);
            Fandevices.Add(tags);
            FanController(Fandevices, _delay);
        }

        #region SetUp Region
        public bool IsControlled { get { return isControlled; } set { isControlled = value; } }

        public bool IsOpened
        {
            get
            {
                if (netServer != null)
                {
                    return netServer.IsRunning;
                }
                else
                {
                    return false;
                }

            }
        }

        public void SendMsg(int addrDev, int function, int addrReg, int RegsCount,
            List<int> data, int label, int random = -1)
        {
            netServer.Send(addrDev, function, addrReg, RegsCount, data, label);
        }


        private byte[] ComputeCRC(List<byte> datalist)
        {
            byte[] table = datalist.ToArray();
            const ushort polynomial = 0xA001;

            byte i, chChar;
            ushort wCRC = 0xFFFF;

            int j = 3;                     // it should start from head instead.
            while (j < table.Length)
            {
                chChar = table[j];
                wCRC ^= chChar;
                for (i = 0; i < 8; i++)
                {
                    if ((wCRC & 0x0001) != 0)
                        wCRC = (ushort)((wCRC >> 1) ^ polynomial);
                    else
                        wCRC >>= 1;
                }

                j++;
            }

            byte[] temp = new byte[2];
            temp[0] = (byte)wCRC;
            temp[1] = (byte)(wCRC >> 8);
            return temp;
        }




        private bool OpenConnection()
        {
            try
            {
                netServer = new AsyncServer(ipAddress, port, 10);

                netServer.UpdateDevice = UpdateDevice;

                netServer.Start();

            }
            catch (Exception e)
            {
                var s = e.ToString();
                return false;
            }
            return true;
        }

        private bool CloseConnection()
        {
            try
            {
                netServer.Stop();
            }
            catch (Exception e)
            {
                var s = e.ToString();
                return false;
            }
            return true;
        }

        public int ClientCount
        {
            get
            {
                if (netServer != null)
                {
                    return netServer.ClientCount;
                }
                else
                {
                    return 0;
                }

            }
        }


        public void StartAuto()
        {
            OpenConnection();
            Task.Factory.StartNew(AutoTask, cancelTokenSource.Token);

            FanStartAuto();
        }

        public void StopAuto()
        {
            cancelTokenSource.Cancel();

            CloseConnection();

            FanStopAuto();
        }
        #endregion


        #region Read Region

        private void AutoTask()
        {
            Thread.Sleep(50);


            while (!cancelTokenSource.IsCancellationRequested)
            {
                AskBoard();
                Thread.Sleep(delay);
            }

            cancelTokenSource = new CancellationTokenSource();
        }

        private void UpdateDevice(Msg msg)
        {
            foreach (var device in devices)
            {
                device.Update(msg);
                // 急停关闭螺旋桨
                if (device is Board)
                {
                    Board board = (Board)device;
                    if (1 == board.Emergence)
                    {
                        Manifest.Emergency = true;
                    }
                    else
                    {
                        Manifest.Emergency = false;
                    }
                }
            }

            FanUpdateDevice(msg);
        }

        public void AskBoard()
        {
            int addrDev = 0x03;
            int function = 0x03;
            int addrReg = 0x81;
            int count = 11;

            SendMsg(addrDev, function, addrReg, count, new List<int>(), 1);
        }

        #endregion


        #region Write Region

        public void ArmOpen()
        {
            int addrDev = 0x03;
            int function = 0x10;
            int addrReg = 0x16;
            int RegsCount = 1;

            List<int> data = new List<int>();
            data.Add(1);

            SendMsg(addrDev, function, addrReg, RegsCount, data, 1);
        }

        public void ArmClose()
        {
            int addrDev = 0x03;
            int function = 0x10;
            int addrReg = 0x16;
            int RegsCount = 1;

            List<int> data = new List<int>();
            data.Add(0);

            SendMsg(addrDev, function, addrReg, RegsCount, data, 1);
        }
        public void BasketMove(bool isMoveUp)
        {
            int addrDev = 0x03;
            int function = 0x10;
            int addrReg = 0x10;
            int RegsCount = 2;
            int direction = 1;

            if (isMoveUp)
            {
                direction = 2;
            }
            List<int> data = new List<int>();
            data.Add(2);
            data.Add(3 * 256 + direction);
            SendMsg(addrDev, function, addrReg, RegsCount, data, 1);
        }

        public void BasketMoveUpDist(double distance)
        {
            double baseposition = (board.LeftHeight + board.RightHeight) / 2;
            double delta = (board.LeftHeight + board.RightHeight) / 2 - baseposition;
            while (delta < distance)
            {
                BasketMove(true);
                Thread.Sleep(delay);
                delta = (board.LeftHeight + board.RightHeight) / 2 - baseposition;
            }
            BasketStop();
        }

        public void BasketMoveUpDist2(double distance)
        {
            distance = distance - 0.01;
            double leftbase = board.LeftHeight;
            double rightbase = board.RightHeight;

            double leftdelta = board.LeftHeight - leftbase;
            double rightdelta = board.RightHeight - rightbase;


            while (leftdelta < distance && rightdelta < distance)
            {
                BasketMove(true);
                Thread.Sleep(10);
                SilenceFans();
                Thread.Sleep(10);
                leftdelta = board.LeftHeight - leftbase;
                rightdelta = board.RightHeight - rightbase;
            }
            BasketStop();
            Thread.Sleep(delay);
            while (leftdelta < distance)
            {
                BasketLeftMove(true);
                Thread.Sleep(20);
                leftdelta = board.LeftHeight - leftbase;
            }

            while (rightdelta < distance)
            {
                BasketRightMove(true);
                Thread.Sleep(20);
                rightdelta = board.RightHeight - rightbase;
            }

            BasketStop();
        }

        public void BasketMoveDownDist(double distance)
        {
            double baseposition = (board.LeftHeight + board.RightHeight) / 2;
            double delta = baseposition - (board.LeftHeight + board.RightHeight) / 2;
            while (delta < distance)
            {
                BasketMove(true);
                Thread.Sleep(delay);
                delta = baseposition - (board.LeftHeight + board.RightHeight) / 2;
            }
            BasketStop();
        }

        public void BasketMoveDownDist2(double distance)
        {
            // distance = distance - 0.01;
            distance = distance - 0.08; // 0.03
            double leftbase = board.LeftHeight;
            double rightbase = board.RightHeight;

            double leftdelta = leftbase - board.LeftHeight;
            double rightdelta = rightbase - board.RightHeight;

            while (leftdelta < distance && rightdelta < distance)
            {
                BasketMove(false);
                Thread.Sleep(10);
                SilenceFans();
                Thread.Sleep(10);
                leftdelta = leftbase - board.LeftHeight;
                rightdelta = rightbase - board.RightHeight;
            }
            BasketStop();
            Thread.Sleep(delay);
            while (leftdelta < distance)
            {
                BasketLeftMove(false);
                Thread.Sleep(20);
                leftdelta = leftbase - board.LeftHeight;
            }

            while (rightdelta < distance)
            {
                BasketRightMove(false);
                Thread.Sleep(20);
                rightdelta = rightbase - board.RightHeight;
            }

            BasketStop();
            Console.WriteLine("Time: " + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss fff") + "----stop basket move to distance!");
        }

        public void BallSwitch(bool toOpen)
        {
            if (toOpen)
            {
                SinglePigment(false);
                Thread.Sleep(50);
                SingleWater(true);
            }
            else
            {
                SingleWater(false);
                Thread.Sleep(50);
                SinglePigment(true);
            }
        }

        public void SingleWater(bool v)
        {
            WaterControl(v);
        }

        public void SingleGas(bool flag)
        {
            GasControl(flag);
        }

        public void BasketLeftMove(bool isMoveUp)
        {
            int addrDev = 0x03;
            int function = 0x10;
            int addrReg = 0x10;
            int RegsCount = 2;
            int direction = 1;

            if (isMoveUp)
            {
                direction = 2;
            }
            List<int> data = new List<int>();
            data.Add(2);
            data.Add(2 * 256 + direction);

            SendMsg(addrDev, function, addrReg, RegsCount, data, 1);
        }

        public void BasketRightMove(bool isMoveUp)
        {
            int addrDev = 0x03;
            int function = 0x10;
            int addrReg = 0x10;
            int RegsCount = 2;
            int direction = 1;

            if (isMoveUp)
            {
                direction = 2;
            }
            List<int> data = new List<int>();
            data.Add(2);
            data.Add(1 * 256 + direction);

            SendMsg(addrDev, function, addrReg, RegsCount, data, 1);
        }

        public void BasketStop()
        {
            int addrDev = 0x03;
            int function = 0x10;
            int addrReg = 0x10;
            int RegsCount = 1;

            List<int> data = new List<int>();
            data.Add(4);

            SendMsg(addrDev, function, addrReg, RegsCount, data, 1);
        }

        public void SlideMove(bool toleft, int speed)
        {
            int addrDev = 0x03;
            int function = 0x10;
            int addrReg = 0x13;
            int RegsCount = 3;

            int direction = 2;

            if (toleft)
            {
                direction = 1;
            }

            List<int> data = new List<int>();
            data.Add(2);
            data.Add(direction);
            data.Add(speed * 256);

            SendMsg(addrDev, function, addrReg, RegsCount, data, 1);
        }

        public void SlideMoveTo(int pos, int speed)
        {
            int addrDev = 0x03;
            int function = 0x10;
            int addrReg = 0x13;
            int RegsCount = 3;
            int acc = 100;
            List<int> data = new List<int>();
            data.Add(3);
            data.Add(pos);
            data.Add(speed * 256 + 100);

            SendMsg(addrDev, function, addrReg, RegsCount, data, 1);
            Thread.Sleep(100);
        }

        public void SprinkerControl(bool toopen)
        {
            Random rd = new Random();
            int randomId = rd.Next(1, Int16.MaxValue);
            LogHelper.WriteLog(string.Format("Sprinker control status: {0},randomId:{1}", toopen, randomId));

            int addrDev = 0x03;
            int function = 0x10;
            int addrReg = 0x18;
            int RegsCount = 1;

            List<int> data = new List<int>();
            if (toopen)
            {
                data.Add(1);
            }
            else
            {
                data.Add(0);
            }
            SendMsg(addrDev, function, addrReg, RegsCount, data, randomId);
        }

        public void GasControl(bool toopen)
        {
            Random rd = new Random();
            int randomId = rd.Next(1, Int16.MaxValue);
            LogHelper.WriteLog(string.Format("Gas control status: {0},randomId:{1}", toopen, randomId));

            int addrDev = 0x03;
            int function = 0x10;
            int addrReg = 0x19;
            int RegsCount = 1;

            List<int> data = new List<int>();
            if (toopen)
            {
                data.Add(1);
            }
            else
            {
                data.Add(0);
            }
            // random 代替标签位
            SendMsg(addrDev, function, addrReg, RegsCount, data, randomId);
        }

        private void PigmentControl(bool toopen)
        {
            int addrDev = 0x03;
            int function = 0x10;
            int addrReg = 0x1B;
            int RegsCount = 1;

            List<int> data = new List<int>();
            if (toopen)
            {
                data.Add(1);
            }
            else
            {
                data.Add(0);
            }

            SendMsg(addrDev, function, addrReg, RegsCount, data, 1);
        }

        private void WaterControl(bool toopen)
        {
            int addrDev = 0x03;
            int function = 0x10;
            int addrReg = 0x1C;
            int RegsCount = 1;

            List<int> data = new List<int>();
            if (toopen)
            {
                data.Add(1);
            }
            else
            {
                data.Add(0);
            }

            SendMsg(addrDev, function, addrReg, RegsCount, data, 1);
        }

        public void SwitchToPigment()
        {
            WaterControl(false);
            Thread.Sleep(50);
            PigmentControl(true);

        }

        public void OpenBaffle()
        {
            int count = 1;
            while (--count >= 0)
            {
                WaterControl(false);
                Thread.Sleep(50);
                PigmentControl(true);
            }
            baffleFlag = true;
        }

        public void ColseBaffle()
        {
            int count = 1;
            while (--count >= 0)
            {
                PigmentControl(false);
                Thread.Sleep(50);
                WaterControl(true);
            }
            baffleFlag = false;
        }

        public void SinglePigment(bool flag)
        {

            PigmentControl(flag);

        }

        public void SwitchToWater()
        {
            PigmentControl(false);
            Thread.Sleep(50);
            WaterControl(true);
        }

        public void CloseWaterAndPigment()
        {
            PigmentControl(false);
            Thread.Sleep(50);
            WaterControl(false);
        }

        public bool CombinedOpen(int delay)
        {
            int count = 0;
            // 下位机返回的喷涂标志
            while (board.Printer != 1)
            {
                GasControl(true);
                Thread.Sleep(delay);
                SprinkerControl(true);
                Thread.Sleep(50);
                count++;
                if (count > 4)
                {
                    return false;
                }
            }
            return true;
        }


        public bool NSCombinedOpen(int delay)
        {
            int count = 0;
            // 下位机返回的喷涂标志
            while (board.Printer != 1)
            {
                GasControl(true);
                Thread.Sleep(delay);
                SprinkerControl(true);
                count++;
                if (count > 4)
                {
                    return false;
                }
            }
            return true;
        }

        public bool NSCombinedClose(int delay)
        {
            int count = 0;
            while (board.Printer != 0)
            {
                SprinkerControl(false);
                // 蓄力
                Thread.Sleep(delay);
                GasControl(false);
                count++;
                if (count > 4)
                {
                    return false;
                }
            }
            return true;
        }

        public bool CombinedClose(int delay)
        {
            int count = 0;
            while (board.Printer != 0)
            {
                SprinkerControl(false);
                Thread.Sleep(delay);
                GasControl(false);
                Thread.Sleep(100);
                count++;
                if (count > 4)
                {
                    return false;
                }
            }
            return true;
        }

        public void ClearError()
        {
            int addrDev = 0x03;
            int function = 0x10;
            int addrReg = 0x17;
            int RegsCount = 1;

            List<int> data = new List<int>();

            data.Add(1);

            SendMsg(addrDev, function, addrReg, RegsCount, data, 1);
        }

        public void HeightClear()
        {
            int addrDev = 0x03;
            int function = 0x10;
            int addrReg = 0x10;
            int RegsCount = 1;

            List<int> data = new List<int>();
            data.Add(1);

            SendMsg(addrDev, function, addrReg, RegsCount, data, 1);

            Thread.Sleep(500);
        }
        #endregion

        #region  Data Region
        public string[] BoardData { get { return board.Values(); } }
        #endregion
    }
}
